<!DOCTYPE html>
<html lang="zh_cn">
<head>
          <title>来玩魔王的咚</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta charset="utf-8" />
        <!-- twitter card metadata -->
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="/images/mowang.png">
<meta name="twitter:site" content="">
<meta name="twitter:title" content="pytest（一）入门">
<meta name="twitter:description" content="<p>使用pytest进行测试的基本用法。使用断言测试；测试异常抛出；测试发现的规则；运行测试的方式；mark标记和基于标记的测试运行。</p>">
        <!-- OG Tags -->
<meta property="og:url" content="/pytest-getting-started.html"/>
<meta property="og:title" content="来玩魔王的咚 | pytest（一）入门" />
<meta property="og:description" content="<p>使用pytest进行测试的基本用法。使用断言测试；测试异常抛出；测试发现的规则；运行测试的方式；mark标记和基于标记的测试运行。</p>" />
        <!-- favicon -->
        <link rel="icon" type="image/png" href="/images/mowang.png">
        <!-- moment.js for date formatting -->
        <script src="/theme/js/moment.js"></script>
        <!-- css -->
        <link rel="stylesheet" type="text/css" href="/theme/css/main.css" />
        <!-- 左边的menu，如果页面高度不够，就跟着滚动，否则文章分类显示不全 -->
        <link rel="stylesheet" type="text/css" href="/theme/css/mycss/menu.css" />
		<script>
			
                /*! grunt-grunticon Stylesheet Loader - v2.1.2 | https://github.com/filamentgroup/grunticon | (c) 2015 Scott Jehl, Filament Group, Inc. | MIT license. */
    
    (function(e){function t(t,n,r,o){"use strict";function a(){for(var e,n=0;u.length>n;n++)u[n].href&&u[n].href.indexOf(t)>-1&&(e=!0);e?i.media=r||"all":setTimeout(a)}var i=e.document.createElement("link"),l=n||e.document.getElementsByTagName("script")[0],u=e.document.styleSheets;return i.rel="stylesheet",i.href=t,i.media="only x",i.onload=o||null,l.parentNode.insertBefore(i,l),a(),i}var n=function(r,o){"use strict";if(r&&3===r.length){var a=e.navigator,i=e.Image,l=!(!document.createElementNS||!document.createElementNS("http://www.w3.org/2000/svg","svg").createSVGRect||!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image","1.1")||e.opera&&-1===a.userAgent.indexOf("Chrome")||-1!==a.userAgent.indexOf("Series40")),u=new i;u.onerror=function(){n.method="png",n.href=r[2],t(r[2])},u.onload=function(){var e=1===u.width&&1===u.height,a=r[e&&l?0:e?1:2];n.method=e&&l?"svg":e?"datapng":"png",n.href=a,t(a,null,null,o)},u.src="",document.documentElement.className+=" grunticon"}};n.loadCSS=t,e.grunticon=n})(this);(function(e,t){"use strict";var n=t.document,r="grunticon:",o=function(e){if(n.attachEvent?"complete"===n.readyState:"loading"!==n.readyState)e();else{var t=!1;n.addEventListener("readystatechange",function(){t||(t=!0,e())},!1)}},a=function(e){return t.document.querySelector('link[href$="'+e+'"]')},c=function(e){var t,n,o,a,c,i,u={};if(t=e.sheet,!t)return u;n=t.cssRules?t.cssRules:t.rules;for(var l=0;n.length>l;l++)o=n[l].cssText,a=r+n[l].selectorText,c=o.split(");")[0].match(/US\-ASCII\,([^"']+)/),c&&c[1]&&(i=decodeURIComponent(c[1]),u[a]=i);return u},i=function(e){var t,o,a;o="data-grunticon-embed";for(var c in e)if(a=c.slice(r.length),t=n.querySelectorAll(a+"["+o+"]"),t.length)for(var i=0;t.length>i;i++)t[i].innerHTML=e[c],t[i].style.backgroundImage="none",t[i].removeAttribute(o);return t},u=function(t){"svg"===e.method&&o(function(){i(c(a(e.href))),"function"==typeof t&&t()})};e.embedIcons=i,e.getCSS=a,e.getIcons=c,e.ready=o,e.svgLoadedCallback=u,e.embedSVG=u})(grunticon,this);
                
                grunticon(["/theme/css/icons.data.svg.css", "/theme/css/icons.data.png.css", "/theme/css/icons.fallback.css"]);
            </script>
        <noscript><link href="/theme/css/icons.fallback.css" rel="stylesheet"></noscript>
        <!-- menu toggle javascript -->
        <script type="text/javascript">
            document.addEventListener("DOMContentLoaded", initMenu);
            
            function initMenu(){
                var menu = document.getElementById("menu");
                var menulink = document.getElementById("menu-link");
                menulink.addEventListener("click", function toggleMenu(){
                        window.event.preventDefault();
                        menulink.classList.toggle('active');
                        menu.classList.toggle('active');              
                    });
            };
        </script>
        <!-- 不蒜子 -->
        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

    <meta name="description" content="<p>使用pytest进行测试的基本用法。使用断言测试；测试异常抛出；测试发现的规则；运行测试的方式；mark标记和基于标记的测试运行。</p>" />

    <meta name="tags" content="测试" />
  <!-- 替换部分base的样式，看文章时，要再宽一点，右边有很多空间可以撑开 -->
  <link rel="stylesheet" type="text/css" href="/theme/css/mycss/article.css" />

</head>
<body>
    <div role="banner" id="masthead">
        <header>
            <a href="/"><img src="/images/mowang.png" alt="McManus Logo"></a>
                <h1>来玩魔王的咚@骑士救兵</h1>
            <a href="#menu" id="menu-link">more stuff</a>
            <nav id="menu">
                <ul>
                        <li><a href="/tags">tags</a></li>
                            <li><a href="/category/cloud.html">Cloud</a></li>
                            <li><a href="/category/docker.html">Docker</a></li>
                            <li><a href="/category/go.html">Go</a></li>
                            <li><a href="/category/linux.html">Linux</a></li>
                            <li class="active"><a href="/category/python.html">Python</a></li>
                            <li><a href="/category/xue-xi-bi-ji.html">学习笔记</a></li>
                            <li><a href="/category/yun-wei-zi-dong-hua.html">运维自动化</a></li>
                </ul>
            </nav>
        </header>
    </div>
        <div class="page" role="main">
  <div class="article" role="article">
    <article>
        <footer>
            <a name="top"></a>
            <p>
              <time datetime=" 2022-03-16 18:55:00+08:00">
                <script>document.write(moment('2022-03-16 18:55:00+08:00').format('LL'));</script>
              </time>
              ~
              <time datetime=" 2022-03-16 18:55:00+08:00">
                <script>document.write(moment('2022-03-28 21:09:00+08:00').format('LL'));</script>
              </time>
            </p>
        </footer>
        <header>
          <h2>
            pytest（一）入门
          </h2>
        </header>
      <div class="content">
         <div class="toc">
<ul>
<li><a href="#yong-duan-yan-zuo-ce-shi">用断言做测试</a><ul>
<li><a href="#duan-yan-shang-xia-wen-min-gan-de-bi-jiao">断言上下文敏感的比较</a></li>
</ul>
</li>
<li><a href="#ce-shi-fa-xian">测试发现</a><ul>
<li><a href="#ding-zhi-ce-shi-fa-xian">定制测试发现</a></li>
</ul>
</li>
<li><a href="#ce-shi-yi-chang-de-pao-chu">测试异常的抛出</a><ul>
<li><a href="#pi-pei-yi-chang-xin-xi">匹配异常信息</a></li>
<li><a href="#zheng-ze-pi-pei-yi-chang-xin-xi">正则匹配异常信息</a></li>
</ul>
</li>
<li><a href="#zai-lei-zhong-ce-shi">在类中测试</a></li>
<li><a href="#yun-xing-ce-shi">运行测试</a><ul>
<li><a href="#zhi-ding-yun-xing-de-ce-shi">指定运行的测试</a></li>
<li><a href="#huo-qu-xin-xi-de-ming-ling-xing-can-shu">获取信息的命令行参数</a></li>
<li><a href="#fen-xi-ce-shi-de-zhi-xing-shi-jian">分析测试的执行时间</a></li>
<li><a href="#guan-li-cha-jian-jia-zai">管理插件加载</a></li>
<li><a href="#python-mo-kuai-diao-yong">python 模块调用</a></li>
<li><a href="#python-dai-ma-diao-yong">python 代码调用</a></li>
</ul>
</li>
<li><a href="#zi-ding-yi-mark-biao-ji">自定义mark标记</a><ul>
<li><a href="#zhu-ce-biao-ji">注册标记</a></li>
<li><a href="#tian-jia-biao-ji">添加标记</a></li>
</ul>
</li>
</ul>
</div>
<h3 id="yong-duan-yan-zuo-ce-shi"><a class="toclink" href="#yong-duan-yan-zuo-ce-shi">用断言做测试</a></h3>
<p>用pytest测试时，只要用基础的assert断言就行了。pytest会重写测试函数的assert断言语句。使用的时候assert用于输出失败消息的第二个参数留空，pytest会填入测试失败的消息。<br>
下面是一个简单的示例：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>

<span class="k">def</span> <span class="nf">test_answer</span><span class="p">():</span>
    <span class="k">assert</span> <span class="n">func</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">4</span>
</code></pre></div>

<h4 id="duan-yan-shang-xia-wen-min-gan-de-bi-jiao"><a class="toclink" href="#duan-yan-shang-xia-wen-min-gan-de-bi-jiao">断言上下文敏感的比较</a></h4>
<p>pytest的raise断言，在遇到上下文敏感的信息时，会提供更多的支持。下面以集合的比较举例：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">test_set_comparison</span><span class="p">():</span>
    <span class="n">set1</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="s2">&quot;13084&quot;</span><span class="p">)</span>
    <span class="n">set2</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="s2">&quot;80356&quot;</span><span class="p">)</span>
    <span class="k">assert</span> <span class="n">set1</span> <span class="o">==</span> <span class="n">set2</span>

<span class="sd">&#39;&#39;&#39;</span>
<span class="sd">AssertionError: assert {&#39;0&#39;, &#39;1&#39;, &#39;3&#39;, &#39;4&#39;, &#39;8&#39;} == {&#39;0&#39;, &#39;3&#39;, &#39;5&#39;, &#39;6&#39;, &#39;8&#39;}</span>
<span class="sd">  Extra items in the left set:</span>
<span class="sd">  &#39;4&#39;</span>
<span class="sd">  &#39;1&#39;</span>
<span class="sd">  Extra items in the right set:</span>
<span class="sd">  &#39;5&#39;</span>
<span class="sd">  &#39;6&#39;</span>
<span class="sd">  Use -v to get the full diff</span>
<span class="sd">def test_set_comparison():</span>
<span class="sd">        set1 = set(&quot;13084&quot;)</span>
<span class="sd">        set2 = set(&quot;80356&quot;)</span>
<span class="sd">&gt;       assert set1 == set2</span>
<span class="sd">E       AssertionError: assert {&#39;0&#39;, &#39;1&#39;, &#39;3&#39;, &#39;4&#39;, &#39;8&#39;} == {&#39;0&#39;, &#39;3&#39;, &#39;5&#39;, &#39;6&#39;, &#39;8&#39;}</span>
<span class="sd">E         Extra items in the left set:</span>
<span class="sd">E         &#39;4&#39;</span>
<span class="sd">E         &#39;1&#39;</span>
<span class="sd">E         Extra items in the right set:</span>
<span class="sd">E         &#39;5&#39;</span>
<span class="sd">E         &#39;6&#39;</span>
<span class="sd">E         Use -v to get the full diff</span>
<span class="sd">&#39;&#39;&#39;</span>
</code></pre></div>

<p>查看详细的输出，会报告两边元素的差异。  </p>
<p>对以下情况会做一些特殊的比较：</p>
<ul>
<li>比较长字符串，显示差异</li>
<li>比较序列，显示第一个失败的地方</li>
<li>比较字典，显示不同的条目</li>
</ul>
<p>总之，功能很大强，使用很简单。用assert在测试的最后做出你想要的断言就好了。  </p>
<h3 id="ce-shi-fa-xian"><a class="toclink" href="#ce-shi-fa-xian">测试发现</a></h3>
<p>pytest会对当前目录及其子目录中，所有的<code>test_*.py</code>或<code>*_test.py</code>文件进行测试。  </p>
<p>上面是比较简单的说明，实际上会遵循下面的测试发现的规则。<br>
Python test discovery 的约定:</p>
<ul>
<li>从<code>testpaths</code>变量定义的路径，或当前目录开始搜索。或者用命令行参数指定目录、文件名或节点ID</li>
<li>对子目录递归，除了<code>norecursedirs</code>中排除掉的</li>
<li>在目录中搜索<code>test_*.py</code>或<code>*_test.py</code>的文件，按包名导入</li>
<li>在这些文件中，收集以下的测试项目:</li>
<li>class之外，以<code>test</code>为前缀的函数</li>
<li>以<code>Test</code>为前缀额类名（不能有<code>__init__</code>方法），且以<code>test</code>为前缀的方法</li>
</ul>
<p>另外，在模块中，还会发现标准的<code>unittest.TestCase</code>定义的类。  </p>
<h4 id="ding-zhi-ce-shi-fa-xian"><a class="toclink" href="#ding-zhi-ce-shi-fa-xian">定制测试发现</a></h4>
<p>可以根据需要，定制发现规则。定制的规则需要写到配置文件中，配置文件有几种形式，这里使用<code>pytest.ini</code>，并且这个配置文件需要放在测试的根目录下。<br>
下面是定义测试文件发现规则的例子：</p>
<div class="highlight"><pre><span></span><code><span class="k">[pytest]</span><span class="w"></span>
<span class="na">python_files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">test_*.py check_*.py example_*.py</span><span class="w"></span>
</code></pre></div>

<p>也可以使用多行形式：</p>
<div class="highlight"><pre><span></span><code><span class="k">[pytest]</span><span class="w"></span>
<span class="na">python_files</span><span class="w"> </span><span class="o">=</span><span class="w"></span>
<span class="w">    </span><span class="na">test_*.py</span><span class="w"></span>
<span class="w">    </span><span class="na">check_*.py</span><span class="w"></span>
<span class="w">    </span><span class="na">example_*.py</span><span class="w"></span>
</code></pre></div>

<p>下面的例子是定制了测试文件，测试函数名称，测试类名称的规则：</p>
<div class="highlight"><pre><span></span><code><span class="k">[pytest]</span><span class="w"></span>
<span class="na">python_files</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">check_* test_*</span><span class="w"></span>
<span class="na">python_functions</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">check_* test_*</span><span class="w"></span>
<span class="na">python_classes</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">Check* Test*</span><span class="w"></span>
</code></pre></div>

<p>这里仅介绍定制测试发现的方式。还有更多配置文件和配置项可以使用，执行pytest的help命令也能获取到这部分的帮助信息：</p>
<div class="highlight"><pre><span></span><code>pytest -h
<span class="c1"># 下面这个标题开始的是有关配置文件的帮助信息</span>
<span class="c1"># [pytest] ini-options in the first pytest.ini|tox.ini|setup.cfg file found:</span>
</code></pre></div>

<h3 id="ce-shi-yi-chang-de-pao-chu"><a class="toclink" href="#ce-shi-yi-chang-de-pao-chu">测试异常的抛出</a></h3>
<p>有时候程序需要主动抛出异常，也有方法来测试程序是否按照预期抛出了异常。<br>
推荐的做法是将<code>pytest.raises()</code>放到with的上下文管理器中，测试是否抛出了预期的异常：</p>
<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">pytest</span>

<span class="k">def</span> <span class="nf">test_zero_division</span><span class="p">():</span>
    <span class="k">with</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="ne">ZeroDivisionError</span><span class="p">):</span>
        <span class="mi">1</span> <span class="o">/</span> <span class="mi">0</span>
</code></pre></div>

<h4 id="pi-pei-yi-chang-xin-xi"><a class="toclink" href="#pi-pei-yi-chang-xin-xi">匹配异常信息</a></h4>
<p>不仅是异常的类型，还可以匹配异常的消息：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">f</span><span class="p">():</span>
    <span class="sd">&quot;&quot;&quot;无限递归&quot;&quot;&quot;</span>
    <span class="n">f</span><span class="p">()</span>

<span class="kn">import</span> <span class="nn">pytest</span>

<span class="k">def</span> <span class="nf">test_recursion_depth</span><span class="p">():</span>
    <span class="k">with</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="ne">RuntimeError</span><span class="p">)</span> <span class="k">as</span> <span class="n">excinfo</span><span class="p">:</span>
        <span class="n">f</span><span class="p">()</span>
    <span class="k">assert</span> <span class="s2">&quot;maximum recursion&quot;</span> <span class="ow">in</span> <span class="nb">str</span><span class="p">(</span><span class="n">excinfo</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
</code></pre></div>

<h4 id="zheng-ze-pi-pei-yi-chang-xin-xi"><a class="toclink" href="#zheng-ze-pi-pei-yi-chang-xin-xi">正则匹配异常信息</a></h4>
<p>直接看使用示例：</p>
<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">myfunc</span><span class="p">():</span>
    <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s2">&quot;Exception 123 raised&quot;</span><span class="p">)</span>

<span class="kn">import</span> <span class="nn">pytest</span>

<span class="k">def</span> <span class="nf">test_match</span><span class="p">():</span>
    <span class="k">with</span> <span class="n">pytest</span><span class="o">.</span><span class="n">raises</span><span class="p">(</span><span class="ne">ValueError</span><span class="p">,</span> <span class="n">match</span><span class="o">=</span><span class="sa">r</span><span class="s2">&quot;.* 123 .*&quot;</span><span class="p">):</span>
        <span class="n">myfunc</span><span class="p">()</span>
</code></pre></div>

<h3 id="zai-lei-zhong-ce-shi"><a class="toclink" href="#zai-lei-zhong-ce-shi">在类中测试</a></h3>
<p>可以把一组测试放到一个类中:</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">TestClass</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">test_lower</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="s2">&quot;this&quot;</span>
        <span class="k">assert</span> <span class="n">x</span> <span class="o">==</span> <span class="n">x</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">test_upper</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="s2">&quot;HELLO&quot;</span>
        <span class="k">assert</span> <span class="n">x</span> <span class="o">==</span> <span class="n">x</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>

    <span class="k">def</span> <span class="nf">test_title</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">x</span> <span class="o">=</span> <span class="s2">&quot;this is string example....wow!!!&quot;</span>
        <span class="n">y</span> <span class="o">=</span> <span class="s2">&quot;This Is String Example....Wow!!!&quot;</span>
        <span class="k">assert</span> <span class="n">x</span><span class="o">.</span><span class="n">title</span><span class="p">()</span> <span class="o">==</span> <span class="n">y</span>

    <span class="k">def</span> <span class="nf">test_swapcase</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="s1">&#39;xXx&#39;</span><span class="p">,</span> <span class="s1">&#39;XxX&#39;</span>
        <span class="k">assert</span>  <span class="n">x</span><span class="o">.</span><span class="n">swapcase</span><span class="p">()</span> <span class="o">==</span> <span class="n">y</span>
</code></pre></div>

<p>在类中的测试有测试隔离，每一个测试使用的是独立的的实例。<br>
但是在类级别添加的属性是类属性，所有测试共享。类属性是可以修改的，会影响到后面执行的测试。测试的顺序是按类中所在行的位置决定的，和名称无关。修改类属性应该不是一个标准的做法。<br>
下面的示例展示了修改类属性会产生的问题：</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">TestClassDemoInstance</span><span class="p">:</span>
    <span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>  <span class="c1"># 在类级别添加的属性是类属性，所有测试共享</span>

    <span class="k">def</span> <span class="nf">test_1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">1</span>  <span class="c1"># 这里是设置了实例的属性，类属性value在这个方法内被覆盖了</span>
        <span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="mi">1</span>

    <span class="k">def</span> <span class="nf">test_2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c1"># 依然能获取到类属性 value = 0</span>
        <span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="mi">0</span>

    <span class="k">def</span> <span class="nf">test_3</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c1"># 这里修改了类属性，但这是一个错误的做法，下面的test_4能看到问题</span>
        <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">2</span>
        <span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="mi">2</span>

    <span class="k">def</span> <span class="nf">test_4</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="c1"># 在运行整个类的测试的时候，类属性会被上面的test_3改掉</span>
        <span class="c1"># 测试执行的顺序是按行排的，上面的测试先执行，和方法名称无关</span>
        <span class="c1"># 但是用的插件 Test Explorer 又是按名称排的。考虑选择合适的后缀使得两边的排列一致会比较好</span>
        <span class="c1"># 单独运行这个测试会报错，因为类属性的value是0</span>
        <span class="c1"># 同一个测试会有两种不同的结果，所以不应该去修改类属性</span>
        <span class="k">assert</span> <span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="mi">2</span>
</code></pre></div>

<h3 id="yun-xing-ce-shi"><a class="toclink" href="#yun-xing-ce-shi">运行测试</a></h3>
<p>通常是通过命令<code>pytest</code>来调用的。这会调用所有的测试。  </p>
<h4 id="zhi-ding-yun-xing-de-ce-shi"><a class="toclink" href="#zhi-ding-yun-xing-de-ce-shi">指定运行的测试</a></h4>
<p>指定module：</p>
<div class="highlight"><pre><span></span><code>pytest test_mod.py
</code></pre></div>

<p>指定目录：</p>
<div class="highlight"><pre><span></span><code>pytest testing/
</code></pre></div>

<p>一般会在VSCode的插件设置里加上这个参数，当然也可以替换成别的，或者是加上别的参数。  </p>
<p>关键字表达式：</p>
<div class="highlight"><pre><span></span><code>pytest -k <span class="s2">&quot;MyClass and not method&quot;</span>
</code></pre></div>

<p>匹配文件名、类名、函数名，不区分大小写。上面的示例会运行<code>TestMyClass.test_something</code>，但是不会运行<code>TestMyClass.test_method_simple</code>。  </p>
<p>nodeid：
每个收集到的测试都会分配一个唯一id。nodeid是模块文件名、类名、方法名、还有参数化名称，这些字段组合，中间用<code>::</code>分隔:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 运行模块中的指定的测试</span>
pytest test_mod.py::test_func
<span class="c1"># 指定了类下的一个测试方法</span>
pytest test_mod.py::TestClass::test_method
</code></pre></div>

<p><strong><a href="#zi-ding-yi-mark-biao-ji">mark标记</a></strong><br>
运行有slow标记的测试:</p>
<div class="highlight"><pre><span></span><code>pytest -m slow
</code></pre></div>

<p>标记有内置标记，还有自定义标记，这种类似于Tag标签的使用场景一般是先定义好自定义标记。然后使用装饰器，比如<code>@pytest.mark.slow</code>添加到测试函数或者类上，也有直接为这个模块（就是整个py文件中的测试）添加标记的方式。<br>
有了标记后，就可以通过标记来筛选了。筛选时还支持and或or的逻辑匹配多个标签：</p>
<div class="highlight"><pre><span></span><code>pytest -m <span class="s1">&#39;slow or smoke&#39;</span>
</code></pre></div>

<p>从别的包导入：</p>
<div class="highlight"><pre><span></span><code>pytest --pyargs pkg.testing
</code></pre></div>

<p>导入包<code>import pkg.testing</code>。  </p>
<h4 id="huo-qu-xin-xi-de-ming-ling-xing-can-shu"><a class="toclink" href="#huo-qu-xin-xi-de-ming-ling-xing-can-shu">获取信息的命令行参数</a></h4>
<p>分别加上参数获取需要的信息:</p>
<div class="highlight"><pre><span></span><code>pytest --version   <span class="c1"># shows where pytest was imported from</span>
pytest --fixtures  <span class="c1"># show available builtin function arguments</span>
pytest -h <span class="p">|</span> --help <span class="c1"># show help on command line and config file options</span>
</code></pre></div>

<p><code>--fixtures</code>会展示内置的固件信息，固件需要之后单独写一篇展开。  </p>
<h4 id="fen-xi-ce-shi-de-zhi-xing-shi-jian"><a class="toclink" href="#fen-xi-ce-shi-de-zhi-xing-shi-jian">分析测试的执行时间</a></h4>
<p>获取执行超过1.0秒的，最慢的10个测试:</p>
<div class="highlight"><pre><span></span><code>pytest --durations<span class="o">=</span><span class="m">10</span> --durations-min<span class="o">=</span><span class="m">1</span>.0
</code></pre></div>

<p>默认持续时间太小(&lt;0.005s)是不显示的，可以添加<code>-vv</code>参数获取详细信息来获得。  </p>
<h4 id="guan-li-cha-jian-jia-zai"><a class="toclink" href="#guan-li-cha-jian-jia-zai">管理插件加载</a></h4>
<h5 id="ti-qian-jia-zai-cha-jian"><a class="toclink" href="#ti-qian-jia-zai-cha-jian">提前加载插件</a></h5>
<p>用<code>-p</code>选项指定要提前加载的插件:</p>
<div class="highlight"><pre><span></span><code>pytest -p mypluginmodule
pytest -p pytest_cov
</code></pre></div>

<h5 id="jin-yong-cha-jian"><a class="toclink" href="#jin-yong-cha-jian">禁用插件</a></h5>
<p>禁用也是<code>-p</code>，在名称前加上<code>no:</code>:</p>
<div class="highlight"><pre><span></span><code>pytest -p no:doctest
</code></pre></div>

<h4 id="python-mo-kuai-diao-yong"><a class="toclink" href="#python-mo-kuai-diao-yong">python 模块调用</a></h4>
<p>可以用python的-m参数调用pytest模块，并可在后面填写参数:</p>
<div class="highlight"><pre><span></span><code>python -m pytest <span class="o">[</span>...<span class="o">]</span>
</code></pre></div>

<p>这和直接用pytest命令基本没差别，但这是通过python启动的，这会把当前的目录添加到<code>sys.path</code>。  </p>
<h4 id="python-dai-ma-diao-yong"><a class="toclink" href="#python-dai-ma-diao-yong">python 代码调用</a></h4>
<p>写在代码中:</p>
<div class="highlight"><pre><span></span><code><span class="n">retcode</span> <span class="o">=</span> <span class="n">pytest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
<span class="c1"># 传入选项和参数</span>
<span class="n">retcode</span> <span class="o">=</span> <span class="n">pytest</span><span class="o">.</span><span class="n">main</span><span class="p">([</span><span class="s2">&quot;-x&quot;</span><span class="p">,</span> <span class="s2">&quot;mytestdir&quot;</span><span class="p">])</span>
</code></pre></div>

<p>指定插件调用:</p>
<div class="highlight"><pre><span></span><code>import pytest
import sys


class MyPlugin:
    def pytest_sessionfinish<span class="o">(</span>self<span class="o">)</span>:
        print<span class="o">(</span><span class="s2">&quot;*** test run reporting finishing&quot;</span><span class="o">)</span>


<span class="k">if</span> <span class="nv">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span>:
    sys.exit<span class="o">(</span>pytest.main<span class="o">([</span><span class="s2">&quot;-qq&quot;</span><span class="o">]</span>, <span class="nv">plugins</span><span class="o">=[</span>MyPlugin<span class="o">()]))</span>
</code></pre></div>

<p>这里只是做了个钩子，执行后可以看到打印的语句，说明钩子已经执行了。  </p>
<h5 id="diao-yong-dang-qian-wen-jian-de-ce-shi"><a class="toclink" href="#diao-yong-dang-qian-wen-jian-de-ce-shi">调用当前文件的测试</a></h5>
<p>可以在测试文件的最后加上如下语句:</p>
<div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">&quot;__main__&quot;</span><span class="p">:</span>
    <span class="n">pytestt</span><span class="o">.</span><span class="n">main</span><span class="p">([</span><span class="vm">__file__</span><span class="p">])</span>
</code></pre></div>

<h3 id="zi-ding-yi-mark-biao-ji"><a class="toclink" href="#zi-ding-yi-mark-biao-ji">自定义mark标记</a></h3>
<p>这部分是关于mark的介绍，应该算是进阶内容了，不过这里也只介绍mark的基础知识，一些和mark相关的其他进阶内容会另外再写一篇。上面提到了相关内容，就在这里把mark的基础知识一并带上。<br>
在pytest中已经内置了一些mark标记，所以自定义标记的名称要避免和内置标记重名。可以用下面的命令查看已有的标记，包括内置的和用户自定义的：</p>
<div class="highlight"><pre><span></span><code>pytest --markers
</code></pre></div>

<p>命令会打印定义的标记和标记的说明内容。所以在自定义标记的时候，也需要提供标记的名称和标记的说明，标记说明可选不过建议加上。  </p>
<p>自定义标记是需要写到配置文件中的。默认只会在根目录中查找配置文件，一般是把配置文件放在测试目录的根目录中，比如tests目录。所以直接运行上面的命令可能也会找不到自定义的标记，因为在运行命令的当前目录下找不到配置文件。这时候需要加上位置参数指定路径：</p>
<div class="highlight"><pre><span></span><code>pytest --markers tests
</code></pre></div>

<p>位置参数指定的路径可以是多个。  </p>
<h4 id="zhu-ce-biao-ji"><a class="toclink" href="#zhu-ce-biao-ji">注册标记</a></h4>
<p>就是在配置文件中添加自定义的标记：</p>
<div class="highlight"><pre><span></span><code><span class="k">[pytest]</span><span class="w"></span>
<span class="na">markers</span><span class="w"> </span><span class="o">=</span><span class="w"></span>
<span class="w">    </span><span class="na">slow: marks tests as slow (deselect with &#39;-m &quot;not slow&quot;&#39;)</span><span class="w"></span>
<span class="w">    </span><span class="na">serial</span><span class="w"></span>
</code></pre></div>

<p>这里注册了两个标记，一个有说明，一个没有说明。还是建议都加上说明。  </p>
<h4 id="tian-jia-biao-ji"><a class="toclink" href="#tian-jia-biao-ji">添加标记</a></h4>
<p>可以为测试函数或者类中的测试方法添加标记：</p>
<div class="highlight"><pre><span></span><code><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">webtest</span>
<span class="k">def</span> <span class="nf">test_send_http</span><span class="p">():</span>
    <span class="k">pass</span>  <span class="c1"># perform some webtest test for your app</span>
</code></pre></div>

<p>为类中所有的测试方法添加标记：</p>
<div class="highlight"><pre><span></span><code><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">webtest</span>
<span class="k">class</span> <span class="nc">TestClass</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">test_startup</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">pass</span>

    <span class="k">def</span> <span class="nf">test_startup_and_more</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">pass</span>
</code></pre></div>

<p>模块级别添加标记，模块中（一般就是当前py文件中）所有的测试添加标记：</p>
<div class="highlight"><pre><span></span><code><span class="c1"># 添加单个标记</span>
<span class="n">pytestmark</span> <span class="o">=</span> <span class="n">pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">webtest</span>
<span class="c1"># 添加多个标记</span>
<span class="n">pytestmark</span> <span class="o">=</span> <span class="p">[</span><span class="n">pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">webtest</span><span class="p">,</span> <span class="n">pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">slowtest</span><span class="p">]</span>
</code></pre></div>

<p>就是将标记传递给模块的全局变量pytestmark，这个方法同样可以用于做类级别的标记。最初是没有类装饰器的，现在建议用装饰器：</p>
<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">TestClass</span><span class="p">:</span>
    <span class="n">pytestmark</span> <span class="o">=</span> <span class="n">pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">webtest</span>
</code></pre></div>

<p>参数化时添加标记：</p>
<div class="highlight"><pre><span></span><code><span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">foo</span>
<span class="nd">@pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">parametrize</span><span class="p">(</span>
    <span class="p">(</span><span class="s2">&quot;n&quot;</span><span class="p">,</span> <span class="s2">&quot;expected&quot;</span><span class="p">),</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="n">pytest</span><span class="o">.</span><span class="n">param</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">marks</span><span class="o">=</span><span class="n">pytest</span><span class="o">.</span><span class="n">mark</span><span class="o">.</span><span class="n">bar</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)]</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">test_increment</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">expected</span><span class="p">):</span>
    <span class="k">assert</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">==</span> <span class="n">expected</span>
</code></pre></div>

<p>这里有两个标记。foo标记对这个测试函数的所有参数化都做了标记。对最后一个参数化单独添加了一个bar标记。  </p>
      </div>
      <div class="back-to-top">
        <a href="/">HOME</a>
        <a href="#top">TOP</a>
      </div>
    </article>
  </div>
<!-- end article -->
<!-- 页面往下滚动一段之后才会显示TOC -->
<script>
  window.onscroll = function() {
    var tocbox = document.getElementsByClassName('toc')[0];
    var osTop = document.documentElement.scrollTop || document.body.scrollTop;
    var osWidth = document.documentElement.scrollWidth || document.body.scrollWidth;
    // console.log(osTop)
    if (osTop>300 && osWidth>865) {
      tocbox.style.display = "block"
    }
    if (osTop<300 || osWidth<865) {
      tocbox.style.display = "none"
    }
  }
</script>
                <footer>
                    <div class="icons">
                    </div>
                    <span id="busuanzi_container_page_pv" style="padding: 10px">本文阅读量<span id="busuanzi_value_page_pv"></span>次</span>
                    <span id="busuanzi_container_site_pv" style="padding: 10px">本站总访问量<span id="busuanzi_value_site_pv"></span>次</span>
                    <span id="busuanzi_container_site_uv" style="padding: 10px">本站总访客数<span id="busuanzi_value_site_uv"></span>人</span>
                    <p>© <script>document.write(moment().format('YYYY'));</script> 749B</p>
                </footer>
        </div>
</body>
</html>